LCA - Toy Model#
A simple demo of LCA
reference: Usher, M., & McClelland, J. L. (2001). The time course of perceptual choice: the leaky, competing accumulator model. Psychological Review, 108(3), 550–592. Retrieved from https://www.ncbi.nlm.nih.gov/pubmed/11488378
adapted from: PrincetonUniversity/PsyNeuLink “””
Setup and Installation:
%%capture
%pip install psyneulink
%pip install seaborn
import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
import psyneulink as pnl
sns.set(style='white', context='poster', font_scale=.8, rc={"lines.linewidth": 2})
np.random.seed(0)
%matplotlib inline
%autosave 5
Autosaving every 5 seconds
# time step
time_step_size = .1
# network params
n_nodes = 3
sd = .2
leak = -1
# Weights
w_input = 1 # w_input (Input to decision layer)
w_cross = .1 # w_cross (Crosstalk input to decision layer)
w_inhib = 1 # w_inhib (Mutual inhibition among decision units)
w_self = 0 # w_self (Self recurrent conn. for each decision unit)
b_dec = 0 # b_dec (Bias input to decision units)
# Weight matrix from Input Layer --> Decision Layer
# Input weights are diagonals, cross weights are off diagonals
identity = np.matrix(np.eye(n_nodes))
mirror_identity = np.ones((n_nodes,n_nodes))
np.fill_diagonal(mirror_identity,0)
input_weights = identity * w_input + mirror_identity * w_cross
# initial states
initial_value = np.zeros((n_nodes,))
# initial_value = np.random.normal(size=(n_nodes,))
input_layer = pnl.TransferMechanism(
size=n_nodes, # Number of units in input layer
initial_value=np.zeros(n_nodes,), # Initial input values
name='INPUT LAYER'
)
decision_layer = pnl.LCAMechanism(
size=n_nodes, # Number of units in input layer
initial_value=initial_value, # Initial input values
time_step_size=time_step_size, # Integration step size
leak=leak, # Sets off diagonals to negative values
self_excitation=w_self, # Set diagonals to self excitate
competition=w_inhib, # Set off diagonals to inhibit
function=pnl.Logistic(x_0=b_dec),
noise=pnl.UniformToNormalDist(standard_deviation=sd).function,
integrator_mode=True,
name='DECISION LAYER'
)
# Set initial output values for decision layer to 0
# for output_state in decision_layer.output_states:
# output_state.value *= 0
decision_process = pnl.Process(
pathway=[input_layer, input_weights, decision_layer],
name='DECISION PROCESS'
)
model = pnl.System(
processes=[decision_process],
reinitialize_mechanisms_when=pnl.Never()
)
---------------------------------------------------------------------------
ComponentError Traceback (most recent call last)
Cell In[4], line 1
----> 1 input_layer = pnl.TransferMechanism(
2 size=n_nodes, # Number of units in input layer
3 initial_value=np.zeros(n_nodes,), # Initial input values
4 name='INPUT LAYER'
5 )
7 decision_layer = pnl.LCAMechanism(
8 size=n_nodes, # Number of units in input layer
9 initial_value=initial_value, # Initial input values
(...) 17 name='DECISION LAYER'
18 )
20 # Set initial output values for decision layer to 0
21 # for output_state in decision_layer.output_states:
22 # output_state.value *= 0
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/globals/parameters.py:592, in check_user_specified.<locals>.check_user_specified_wrapper(self, *args, **kwargs)
589 self._prev_constructor = constructor
591 self._prev_kwargs = kwargs
--> 592 return func(self, *args, **orig_kwargs)
File <@beartype(psyneulink.core.components.mechanisms.processing.transfermechanism.TransferMechanism.__init__) at 0x7fed3bdefb00>:176, in __init__(__beartype_object_140656886792032, __beartype_get_violation, __beartype_conf, __beartype_object_140657004710464, __beartype_object_140661315758464, __beartype_object_140662447297448, __beartype_object_140656885631472, __beartype_object_140662447297560, __beartype_object_140656885631536, __beartype_object_140656885631600, __beartype_object_140656885631664, __beartype_object_140656886808448, __beartype_object_140656896334400, __beartype_object_140657004666736, __beartype_object_140657004713968, __beartype_object_140657004714032, __beartype_object_140657004666816, __beartype_object_140657004714096, __beartype_object_140657004666896, __beartype_args_name_keywordable, __beartype_check_meta, __beartype_func, *args, **kwargs)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/mechanisms/processing/transfermechanism.py:1365, in TransferMechanism.__init__(self, default_variable, input_shapes, input_ports, function, noise, clip, integrator_mode, integrator_function, initial_value, integration_rate, on_resume_integrator_mode, termination_measure, termination_threshold, termination_comparison_op, output_ports, params, name, prefs, **kwargs)
1361 initial_value = self._parse_arg_initial_value(initial_value)
1363 self._current_variable_index = 0
-> 1365 super(TransferMechanism, self).__init__(
1366 default_variable=default_variable,
1367 input_shapes=input_shapes,
1368 input_ports=input_ports,
1369 output_ports=output_ports,
1370 initial_value=initial_value,
1371 noise=noise,
1372 integration_rate=integration_rate,
1373 integrator_mode=integrator_mode,
1374 clip=clip,
1375 termination_measure=termination_measure,
1376 termination_threshold=termination_threshold,
1377 termination_comparison_op=termination_comparison_op,
1378 integrator_function=integrator_function,
1379 on_resume_integrator_mode=on_resume_integrator_mode,
1380 function=function,
1381 params=params,
1382 name=name,
1383 prefs=prefs,
1384 **kwargs
1385 )
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/globals/parameters.py:592, in check_user_specified.<locals>.check_user_specified_wrapper(self, *args, **kwargs)
589 self._prev_constructor = constructor
591 self._prev_kwargs = kwargs
--> 592 return func(self, *args, **orig_kwargs)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/mechanisms/processing/processingmechanism.py:293, in ProcessingMechanism_Base.__init__(self, default_variable, input_shapes, input_ports, function, output_ports, params, name, prefs, context, **kwargs)
270 @check_user_specified
271 def __init__(self,
272 default_variable=None,
(...) 281 **kwargs
282 ):
283 """Abstract class for processing mechanisms
284
285 :param variable: (value)
(...) 290 :param context: (str)
291 """
--> 293 super().__init__(default_variable=default_variable,
294 input_shapes=input_shapes,
295 input_ports=input_ports,
296 function=function,
297 output_ports=output_ports,
298 params=params,
299 name=name,
300 prefs=prefs,
301 context=context,
302 **kwargs
303 )
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/globals/parameters.py:592, in check_user_specified.<locals>.check_user_specified_wrapper(self, *args, **kwargs)
589 self._prev_constructor = constructor
591 self._prev_kwargs = kwargs
--> 592 return func(self, *args, **orig_kwargs)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/mechanisms/mechanism.py:1777, in Mechanism_Base.__init__(self, default_variable, input_shapes, input_ports, input_labels, function, output_ports, output_labels, params, name, prefs, context, **kwargs)
1771 from psyneulink.core.components.ports.outputport import OutputPort
1772 register_category(entry=OutputPort,
1773 base_class=Port_Base,
1774 registry=self._portRegistry,
1775 )
-> 1777 super(Mechanism_Base, self).__init__(
1778 default_variable=default_variable,
1779 input_shapes=input_shapes,
1780 function=function,
1781 param_defaults=params,
1782 prefs=prefs,
1783 name=name,
1784 input_ports=input_ports,
1785 output_ports=output_ports,
1786 input_labels_dict=input_labels,
1787 output_labels_dict=output_labels,
1788 **kwargs
1789 )
1791 # FIX: 10/3/17 - IS THIS CORRECT? SHOULD IT BE INITIALIZED??
1792 self._status = INITIALIZING
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/globals/parameters.py:592, in check_user_specified.<locals>.check_user_specified_wrapper(self, *args, **kwargs)
589 self._prev_constructor = constructor
591 self._prev_kwargs = kwargs
--> 592 return func(self, *args, **orig_kwargs)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/shellclasses.py:82, in Mechanism.__init__(self, default_variable, input_shapes, function, param_defaults, name, prefs, **kwargs)
73 @check_user_specified
74 def __init__(self,
75 default_variable=None,
(...) 80 prefs=None,
81 **kwargs):
---> 82 super().__init__(default_variable=default_variable,
83 input_shapes=input_shapes,
84 function=function,
85 param_defaults=param_defaults,
86 name=name,
87 prefs=prefs,
88 **kwargs)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/globals/parameters.py:592, in check_user_specified.<locals>.check_user_specified_wrapper(self, *args, **kwargs)
589 self._prev_constructor = constructor
591 self._prev_kwargs = kwargs
--> 592 return func(self, *args, **orig_kwargs)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/component.py:1205, in Component.__init__(self, default_variable, param_defaults, input_shapes, function, name, reset_stateful_function_when, prefs, function_params, **kwargs)
1202 else:
1203 self.reset_stateful_function_when = Never()
-> 1205 parameter_values, function_params = self._parse_arguments(
1206 default_variable, param_defaults, input_shapes, function, function_params, kwargs
1207 )
1209 self._initialize_parameters(
1210 context=context,
1211 **parameter_values
1212 )
1214 var = call_with_pruned_args(
1215 self._handle_default_variable,
1216 **parameter_values
1217 )
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/component.py:2310, in Component._parse_arguments(self, default_variable, param_defaults, input_shapes, function, function_params, kwargs)
2305 function_params = {
2306 **kwargs,
2307 **function_params
2308 }
2309 else:
-> 2310 self._validate_arguments(parameter_values)
2312 # self.parameters here still references <class>.parameters, but
2313 # only unchanging attributes are needed here
2314 for p in self.parameters:
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/psyneulink/core/components/component.py:2255, in Component._validate_arguments(self, parameter_values)
2253 # raise deprecated argument errors
2254 if len(deprecated_args) > 0:
-> 2255 raise create_illegal_argument_error([
2256 f"'{arg}' is deprecated. Use '{new_arg}' instead"
2257 for arg, new_arg in deprecated_args.items()
2258 ])
2260 # raise generic illegal argument error
2261 unknown_args = illegal_passed_args.difference(unused_constructor_args)
ComponentError: INPUT LAYER: Illegal argument in constructor (type: TransferMechanism):
'size' is deprecated. Use 'input_shapes' instead
model.show_graph(output_fmt = 'jupyter')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[5], line 1
----> 1 model.show_graph(output_fmt = 'jupyter')
NameError: name 'model' is not defined
input_pattern_set = list(np.eye(n_nodes))
num_time_steps = 80
trials = 2
total_trial_length = trials * num_time_steps
stimuli = np.concatenate([
np.repeat(np.array([pattern_i]), num_time_steps, axis=0)
for pattern_i in input_pattern_set
])
# stimuli = np.concatenate([np.zeros((num_time_steps,n_nodes)) for _ in range(trials)])
# assign inputs to input_layer (Origin Mechanism) for each trial
stim_list_dict = {input_layer: stimuli}
# print out the inputs
print(input_pattern_set)
# run the system
r = model.run(inputs=stim_list_dict, num_trials=total_trial_length)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[6], line 14
6 stimuli = np.concatenate([
7 np.repeat(np.array([pattern_i]), num_time_steps, axis=0)
8 for pattern_i in input_pattern_set
9 ])
11 # stimuli = np.concatenate([np.zeros((num_time_steps,n_nodes)) for _ in range(trials)])
12
13 # assign inputs to input_layer (Origin Mechanism) for each trial
---> 14 stim_list_dict = {input_layer: stimuli}
16 # print out the inputs
17 print(input_pattern_set)
NameError: name 'input_layer' is not defined
sns.set_palette("colorblind")
# compute event bonds
bonds = np.arange(num_time_steps, total_trial_length, num_time_steps)
# fetch activities (n_time_points x n_neurons)
acts = np.squeeze(model.results)
# plot the dynamics
f, ax = plt.subplots(1,1, figsize = (12,5))
ax.plot(acts)
ax.set_title(f'Leaky, competitive dynamics across {n_nodes} units')
ax.set_xlabel('Time')
ax.set_ylabel('Activation')
f.legend([f'unit {i}' for i in range(n_nodes)], frameon=False, bbox_to_anchor=(1.1,.6))
for bond in bonds:
ax.axvline(bond, color='grey', alpha=.5, linestyle='--')
f.tight_layout()
sns.despine()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[7], line 6
3 bonds = np.arange(num_time_steps, total_trial_length, num_time_steps)
5 # fetch activities (n_time_points x n_neurons)
----> 6 acts = np.squeeze(model.results)
8 # plot the dynamics
9 f, ax = plt.subplots(1,1, figsize = (12,5))
NameError: name 'model' is not defined